package com.example.lottery_system.service.mq;

import cn.hutool.core.date.DateUtil;
import com.example.lottery_system.common.errcode.ServiceErrorCodeConstant;
import com.example.lottery_system.common.exception.ControllerException;
import com.example.lottery_system.common.exception.ServiceException;
import com.example.lottery_system.common.utils.JacksonUtil;
import com.example.lottery_system.common.utils.MailUtil;
import com.example.lottery_system.common.utils.SMSUtil;
import com.example.lottery_system.controller.param.DrawPrizeParam;
import com.example.lottery_system.controller.param.Winner;
import com.example.lottery_system.dao.DataObject.ActivityPrizeDO;
import com.example.lottery_system.dao.DataObject.WinningRecordDO;
import com.example.lottery_system.dao.Mapper.ActivityPrizeMapper;
import com.example.lottery_system.dao.Mapper.WinningRecordMapper;
import com.example.lottery_system.service.ActivityService;
import com.example.lottery_system.service.DrawPrizeService;
import com.example.lottery_system.service.Enum.ActivityPrizeStatusEnum;
import com.example.lottery_system.service.Enum.ActivityPrizeTiersEnum;
import com.example.lottery_system.service.Enum.ActivityStatusEnum;
import com.example.lottery_system.service.Enum.ActivityUserStatusEnum;
import com.example.lottery_system.service.activitystatus.ActivityStatusManager;
import com.example.lottery_system.service.dto.DrawPrizeStatusDto;
import jakarta.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.example.lottery_system.common.config.DirectRabbitConfig.QUEUE_NAME;

@Component
@RabbitListener(queues = QUEUE_NAME)
public class MqReceiver {

    Logger logger = LoggerFactory.getLogger(MqReceiver.class);
    @Autowired
    ActivityService activityService;

    @Autowired
    ThreadPoolTaskExecutor poolTaskExecutor;

    @Autowired
    ActivityStatusManager activityStatusManager;

    @Autowired
    DrawPrizeService drawPrizeService;


    @Autowired
    MailUtil mailUtil;

    @Autowired
    SMSUtil smsUtil;

    @Autowired
    WinningRecordMapper winningRecordMapper;

    @Resource
    ActivityPrizeMapper activityPrizeMapper;


    @RabbitHandler
    public void process(Map<String, String> message) throws Exception {
        logger.info("成功接收到mq的消息message：{}", JacksonUtil.writeValueAsString(message));
        String messageData = message.get("messageData");
        DrawPrizeParam param = JacksonUtil.readValue(messageData, DrawPrizeParam.class);

        try {

            // 检查参数
            if (!drawPrizeService.checkDrawPrizeParam(param)) {
                return;
            }
            //        进行状态扭转
            statusConvert(param);

            // 保存中奖记录
            List<WinningRecordDO> winningRecordDOList =  drawPrizeService.saveRecord(param);

            // 异步发送中奖用户短信+邮件
            asyinform(winningRecordDOList);
        } catch (Exception e) {

            // 进行回滚操作， 保证事务的一致性
            rollBack(param);

            throw new ServiceException(ServiceErrorCodeConstant.DRAW_QUEUE_ERROR);
        }

    }

    private void rollBack(DrawPrizeParam param) {
        // 首先回滚人员,奖品，活动的状态
        if (null == param) {
            logger.warn("rollBack：：DrawPrizeParam为空!");
            return;
        }

        // 首先判断是否需要回滚相关的状态
        if (!needRollBackStatus(param)) {
            return;
        }

        // 进行状态的回滚
        rollBackStatus(param);

//        判断是否需要回滚中奖名单
        if (!needRollBackRecord(param)) {
            return;
        }

//        回滚数据库的中奖记录
        rollBackWinnerRecord(param);

    }

    private boolean needRollBackRecord(DrawPrizeParam param) {
        // 查询数据库
        int count = winningRecordMapper.selectCountByAP(param.getActivityId(),param.getPrizeId());

        return count > 0;
    }


    private void rollBackWinnerRecord(DrawPrizeParam param) {
        // 还是以活动id和活动奖品为前提
//        直接删除库表信息
        winningRecordMapper.deleteDOByAP(param.getActivityId(),param.getPrizeId());
    }

    private void rollBackStatus(DrawPrizeParam param) {
//        构造逆转状态参数
        DrawPrizeStatusDto drawPrizeStatusDto = new DrawPrizeStatusDto(
                param.getActivityId(),
                ActivityStatusEnum.RUNNING,
                param.getPrizeId(),
                ActivityPrizeStatusEnum.RUNNING,
                param.getWinnerList().stream().map(Winner::getUserId).collect(Collectors.toList()),
                ActivityUserStatusEnum.RUNNING
        );

        activityStatusManager.rollBackEvent(drawPrizeStatusDto);
    }

    private boolean needRollBackStatus(DrawPrizeParam param) {
//        从上述情况中可以得出，此处只需要判断奖品状态是否改变
//        由于是以奖品为维度的进行的抽奖
//        当奖品状态改变了，其他的状态也必然改变了
//        此处就需要回滚所有的状态
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByPrizeId(param.getActivityId(), param.getPrizeId());
//        如果处于进行中就不需要回滚
        if (activityPrizeDO.getStatus().equalsIgnoreCase(ActivityPrizeStatusEnum.RUNNING.name())) {
            return false;
        }

        return true;

    }

    private void asyinform(List<WinningRecordDO> winningRecordDOList) {
//        建立多线程异常处理
        poolTaskExecutor.execute(()->sendMessageByPhone(winningRecordDOList));
        poolTaskExecutor.execute(()->sendEmail(winningRecordDOList));
    }

    public void sendMessageByPhone(List<WinningRecordDO> winningRecordDOList) {
        // 判空
        if (CollectionUtils.isEmpty(winningRecordDOList)) {
            logger.warn("sendMessageByPhone::winningRecordDOList为空!");
            return;
        }


        for (WinningRecordDO winningRecordDO:winningRecordDOList) {
            Map<String,Object> map = new HashMap<>();
            map.put("name",winningRecordDO.getWinnerName());
            map.put("activityName",winningRecordDO.getActivityName());
            map.put("prizeTiers",winningRecordDO.getPrizeTier());
            map.put("prizeName",winningRecordDO.getPrizeName());
            map.put("winningTime",DateUtil.formatTime(winningRecordDO.getWinningTime()));
            // 进行发送
            try {
                smsUtil.sendMessage("SMS_480915071",
                        winningRecordDO.getWinnerPhoneNumber().getPhone(),
                        JacksonUtil.writeValueAsString(map));
            } catch (Exception e) {
                logger.error("中奖短信发送失败!", e);
                return;
            }
        }


    }

    private void sendEmail(List<WinningRecordDO> winningRecordDOList) {
//        判空
        if (CollectionUtils.isEmpty(winningRecordDOList)) {
            logger.warn("sendEmail::winningRecordDOList为空!");
            return;
        }

        for (WinningRecordDO winningRecordDO: winningRecordDOList) {
            String context = "Hi，" + winningRecordDO.getWinnerName() + "先生/女士。 恭喜您在“"
                    + winningRecordDO.getActivityName() + "”活动中获得"
                    + ActivityPrizeTiersEnum.forName(winningRecordDO.getPrizeTier()).getMsg()
                    + "————《" + winningRecordDO.getPrizeName() + "》。获奖时间为"
                    + DateUtil.formatTime(winningRecordDO.getWinningTime()) + "，请尽快领取您的奖励！";
            try {
                Boolean flg = mailUtil.sendSampleMail(winningRecordDO.getWinnerEmail(), "中奖通知", context);
                if (!flg) {
                    logger.error("发送邮件失败!");
                    return;
                }
            } catch (Exception e) {
                logger.error("发送邮件失败!");
                return;
            }
        }


    }

    private void statusConvert(DrawPrizeParam param) {
        List<Long> userIds = param.getWinnerList().stream().map(Winner::getUserId).collect(Collectors.toList());

        DrawPrizeStatusDto drawPrizeStatusDto = new DrawPrizeStatusDto(
                param.getActivityId(),
                ActivityStatusEnum.COMPLETED,
                param.getPrizeId(),
                ActivityPrizeStatusEnum.COMPLETED,
                userIds,
                ActivityUserStatusEnum.COMPLETED
        );

        activityStatusManager.handlerEvent(drawPrizeStatusDto);
    }
}
